/**
 * 
 */
package com.dalonedrow.rpg.base.systems;

import com.dalonedrow.pooled.PooledStringBuilder;
import com.dalonedrow.pooled.StringBuilderPool;
import com.dalonedrow.rpg.base.flyweights.BaseInteractiveObject;
import com.dalonedrow.rpg.base.flyweights.ScriptTimer;
import com.dalonedrow.rpg.base.flyweights.Scriptable;

/**
 * The Script controller executes all in-game scripts.
 * @author Donald
 * @param <IO>
 * @param <TIMER>
 */
@SuppressWarnings("rawtypes")
public abstract class Script<IO extends BaseInteractiveObject, TIMER extends ScriptTimer<IO>> {
	/** the number of active timers in play. */
	private int	activeTimers	= 0;
	/** the maximum number of timer scripts. */
	private int	maxTimerScript	= 0;
	/**
	 * Adds an IO to a specific group.
	 * @param io the IO
	 * @param group the group name
	 */
	public final void addToGroup(final IO io, final String group) {
		if (io != null
				&& group != null) {
			io.addGroup(group);
		}
	}
	/**
	 * Count the number of active script timers.
	 * @return <code>int</code>
	 */
	public final int countTimers() {
		return activeTimers;
	}
	public abstract void eventStackClear();
	public abstract void eventStackClearForIo(IO io);
	public abstract void eventStackExecute() throws Exception;
	public abstract void eventStackExecuteAll() throws Exception;
	public abstract void eventStackInit();
	/**
	 * Gets the number of active timers in play.
	 * @return <code>int</code>
	 */
	public final int getActiveTimers() {
		return activeTimers;
	}
	/**
	 * Gets the EVENT_SENDER global.
	 * @return {@link BaseInteractiveObject}
	 */
	public abstract IO getEventSender();
	public abstract IO getIOMaxEvents() throws Exception;
	public abstract IO getIOMaxEventsSent() throws Exception;
	/**
	 * Gets the maximum number of timer scripts.
	 * @return <code>int</code>
	 */
	public final int getMaxTimerScript() {
		return maxTimerScript;
	}
	/**
	 * Gets the script timers.
	 * @return {@link TIMER[]}
	 */
	public abstract TIMER[] getScriptTimers();
	/**
	 * Gets the id of a named script assigned to a specific IO.
	 * @param io the IO
	 * @param name the script's name
	 * @return the script's id, if found. If no script exists, -1 is returned
	 */
	public final int getSystemIOScript(final IO io, final String name) {
		int index = -1;
		if (activeTimers > 0) {
			for (int i = 0; i < maxTimerScript; i++) {
				TIMER[] scriptTimers = getScriptTimers();
				if (scriptTimers[i].exists()) {
					if (scriptTimers[i].getIo().equals(io)
							&& scriptTimers[i].getName().equalsIgnoreCase(
									name)) {
						index = i;
						break;
					}
				}
			}
		}
		return index;
	}
	/**
	 * Determines if an IO is in a specific group.
	 * @param io the IO
	 * @param group the group name
	 * @return true if the IO is in the group; false otherwise
	 */
	public final boolean isIOInGroup(final IO io, final String group) {
		boolean val = false;
		for (int i = 0; i < io.getNumIOGroups(); i++) {
			if (group.equalsIgnoreCase(io.getIOGroup(i))) {
				val = true;
				break;
			}
		}
		return val;
	}
	/**
	 * Removes an IO from all groups to which it was assigned.
	 * @param io the IO
	 */
	public final void releaseAllGroups(final IO io) {
		while (io != null
				&& io.getNumIOGroups() > 0) {
			io.removeGroup(io.getIOGroup(0));
		}
		io.setNumIOGroups(0);
	}
	/**
	 * Releases an event, clearing all variables.
	 * @param event the scriptable event
	 */
	public final void releaseScript(final Scriptable<IO> event) {
		if (event != null) {
			if (event.getNumberOfLocalVars() > 0) {
				for (int i = 0; i < event.getNumberOfLocalVars(); i++) {
					event.getLocalVariable(i).setText(null);
					event.setLocalVariable(i, null);
				}
				event.setNumberOfLocalVars(0);
			}
		}
	}
	/**
	 * Removes an IO from a group.
	 * @param io the IO
	 * @param group the group
	 */
	public final void removeGroup(final IO io, final String group) {
		if (io != null
				&& group != null) {
			io.removeGroup(group);
		}
	}
	/**
	 * Sends a script event to an interactive object. The returned value is a
	 * flag indicating whether the event was successful or refused.
	 * @param target the reference id of the targeted io
	 * @param msg the message being sent
	 * @param params the list of parameters applied.
	 * @param eventname the name of the event
	 * @return <code>int</code>
	 * @throws Exception if an error occurs
	 */
	public abstract int sendIOScriptEvent(IO target, int msg, Object[] params,
			String eventname) throws Exception;
	public abstract int sendMsgToAllIO(int msg, Object[] dat) throws Exception;
	public abstract int sendScriptEvent(final Scriptable<IO> event,
			final int msg, final Object[] params, final IO io,
			final String evname, final int info);
	/**
	 * Sets the number of active timers in play.
	 * @param val the number of active timers in play to set
	 */
	public final void setActiveTimers(int val) {
		this.activeTimers = val;
	}
	/**
	 * Sets the EVENT_SENDER global.
	 * @param newSender the new event sender
	 */
	public abstract void setEventSender(IO newSender);
	/**
	 * Sets the main event for an IO.
	 * @param io the IO
	 * @param newevent the event's name
	 */
	public final void setMainEvent(final IO io, final String newevent) {
		if (io == null) {
			return;
		}
		if ("MAIN".equalsIgnoreCase(newevent)) {
			io.setMainevent(null);
		} else {
			io.setMainevent(newevent);
		}
	}
	/**
	 * Sets the maximum number of timer scripts.
	 * @param val the maximum number of timer scripts to set
	 */
	protected final void setMaxTimerScript(final int val) {
		maxTimerScript = val;
	}
	public abstract void timerCheck();
	/** Clears all timers in play. */
	public final void timerClearAll() {
		if (activeTimers > 0) {
			for (int i = 0; i < maxTimerScript; i++) {
				timerClearByNum(i);
			}
		}
		activeTimers = 0;
	}
	public final void timerClearAllLocalsForIO(final IO io) {
		TIMER[] scriptTimers = getScriptTimers();
		for (int i = 0; i < maxTimerScript; i++) {
			if (scriptTimers[i].exists()) {
				if (scriptTimers[i].getIo().equals(io)
						&& scriptTimers[i].getEs().equals(io.getOverscript())) {
					timerClearByNum(i);
				}
			}
		}
	}
	public final void timerClearByIO(final IO io) {
		TIMER[] scriptTimers = getScriptTimers();
		for (int i = 0; i < maxTimerScript; i++) {
			if (scriptTimers[i].exists()) {
				if (scriptTimers[i].getIo().equals(io)) {
					timerClearByNum(i);
				}
			}
		}
	}
	public final void timerClearByNameAndIO(final String timername,
			final IO io) {
		TIMER[] scriptTimers = getScriptTimers();
		for (int i = 0; i < maxTimerScript; i++) {
			if (scriptTimers[i].exists()) {
				if (scriptTimers[i].getIo().equals(io)
						&& timername.equalsIgnoreCase(
								scriptTimers[i].getName())) {
					timerClearByNum(i);
				}
			}
		}
	}
	/**
	 * Clears a timer by its index on the timers list.
	 * @param timeridx the index
	 */
	public void timerClearByNum(final int timeridx) {
		TIMER[] scriptTimers = getScriptTimers();
		if (scriptTimers[timeridx].exists()) {
			scriptTimers[timeridx].setName(null);
			activeTimers--;
			scriptTimers[timeridx].setNamelength(0);
			scriptTimers[timeridx].setExists(false);
		}
	}
	/**
	 * Clears all timers set for a specific IO.
	 * @param io the IO
	 */
	public final void timerClearForIO(final IO io) {
		TIMER[] scriptTimers = getScriptTimers();
		for (int i = 0; i < maxTimerScript; i++) {
			if (scriptTimers[i].exists()) {
				if (scriptTimers[i].getIo().equals(io)) {
					timerClearByNum(i);
				}
			}
		}
	}
	/**
	 * Determines whether a specific named timer exists.
	 * @param texx the timer's name
	 * @return the timer's index if it exists, otherwise returns -1
	 */
	private int timerExist(final String texx) {
		int index = -1;
		TIMER[] scriptTimers = getScriptTimers();
		for (int i = 0; i < maxTimerScript; i++) {
			if (scriptTimers[i].exists()) {
				if (scriptTimers[i].getName() != null
						&& scriptTimers[i].getName().equalsIgnoreCase(texx)) {
					index = i;
					break;
				}
			}
		}

		return index;
	}
	// public abstract void releaseScript(SCRIPT es);
	/**
	 * Initializes all game timers.
	 * @param number the maximum number of timers used. Must be at least 100.
	 */
	public abstract void timerFirstInit(int number);
	/**
	 * Generates a random name for an unnamed timer.
	 * @return {@link String}
	 */
	private String timerGetDefaultName() {
		int i = 1;
		String texx;

		while (true) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("TIMER_");
			sb.append(i);
			i++;

			if (timerExist(sb.toString()) == -1) {
				texx = sb.toString();
				sb.returnToPool();
				break;
			}
			sb.returnToPool();
		}
		return texx;
	}
	/**
	 * Gets the index of a free script timer.
	 * @return <code>int</code>
	 */
	public int timerGetFree() {
		int index = -1;
		TIMER[] scriptTimers = getScriptTimers();
		for (int i = 0; i < maxTimerScript; i++) {
			if (!scriptTimers[i].exists()) {
				index = i;
				break;
			}
		}
		return index;
	}
}
